package spider

import (
	"asocrawler/conf"
	"asocrawler/db"
	"asocrawler/tool"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"runtime/debug"
	"time"
)

const (
	bilibiliApiUrl = "https://api.bilibili.com/x/web-interface/ranking/v2?rid=0&type=all"
)

type BilibiliSpider struct {
	ChannelName  string
	TimeInterval int
	Platform     string
}

func NewBilibiliSpider() (v *BilibiliSpider) {
	v = &BilibiliSpider{
		ChannelName:  "B站",
		Platform:     "B站",
		TimeInterval: 2,
	}
	return
}

func (this BilibiliSpider) Run() {
	go func() {
		this.run()
	}()
}
func (this *BilibiliSpider) Println(v ...interface{}) {
	var str = fmt.Sprintf("[%s]-[%s]", this.ChannelName, this.Platform)
	if conf.Conf.Debug.Switch {
		str += "-[Debug]"
	}
	log.Println(str, fmt.Sprintln(v...))
}

type BilibiliResult struct {
	Code int `json:"code"`
	Data struct {
		List []struct {
			Bvid       string `json:"bvid"`       // 视频id
			Title      string `json:"title"`      // 视频标题
			Pic        string `json:"pic"`        // 封面
			Ctime      int64  `json:"ctime"`      // 发布时间 时间戳
			Short_link string `json:"short_link_v2"` // 视频源链接
			Tname      string `json:"tname"`      // 视频分类
			Owner      struct {
				Mid  int    `json:"mid"`  // 作者唯一id
				Face string `json:"face"` // 作者头像
				Name string `json:"name"` // 作者名称
			} `json:"owner"` // 作者信息
			Stat struct {
				View  int `json:"view"`  // 播放
				Share int `json:"share"` // 分享
				Like  int `json:"like"`  // 点赞
				Reply int `json:"reply"` // 评论
			} `json:"stat"` // 统计信息
		} `json:"list"`
	} `json:"data"`
}

func (this *BilibiliSpider) collectVideoData(date string) (rs []db.NewRankVideoResult, err error) {
	tryTimes := 0
	client := &http.Client{}

re:
	req, err := http.NewRequest("GET", bilibiliApiUrl, nil)
	if err != nil {
		return
	}

	resp, err := client.Do(req)
	if err != nil {
		time.Sleep(1 * time.Second)
		return
	}

	defer resp.Body.Close()

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	var p BilibiliResult
	err = json.Unmarshal(b, &p)
	if err != nil {
		this.Println("collectVideoData error,err=", err, string(b))
		return
	}
	if p.Code != 0 {
		if tryTimes < 10 {
			this.Println("collectVideoData p.Code != 0,尝试重新请求...tryTimes：", tryTimes)
			tryTimes++
			time.Sleep(500 * time.Millisecond)
			goto re
		}
		err = errors.New("p.Code != 0,p=" + string(b))
		return
	}

	for _, d := range p.Data.List {

		vPlatform := this.Platform
		videoId := d.Bvid + "-" + vPlatform
		publishTime := time.Unix(d.Ctime, 0).Format("2006-01-02 15:04:05")

		// 作者id
		userId := fmt.Sprint(d.Owner.Mid) + "-" + vPlatform

		link := ""
		if d.Short_link == "" {
			link = "https://b23.tv/" + d.Bvid
		} else {
			link = d.Short_link
		}

		r := db.NewRankVideoResult{
			VideoId:       videoId,
			VideoTitle:    d.Title,
			VideoPlatform: vPlatform,
			CoverUrl:      d.Pic,
			PublishTime:   publishTime,
			OriginUrl:     link,

			UserId:       userId,
			UserAvatar:   d.Owner.Face,
			UserNickName: d.Owner.Name,
			UserType:     d.Tname,

			TotalShareCount:   d.Stat.Share,
			TotalCommentCount: d.Stat.Reply,
			TotalDiggCount:    d.Stat.Like,
			TotalViewCount:    d.Stat.View,

			Date: date,
		}

		rs = append(rs, r)
	}
	return
}

func (this *BilibiliSpider) Collect(date string) (bool, error) {
	rs, err := this.collectVideoData(date)
	if err != nil {
		return false, err
	}

	if len(rs) == 0 {
		this.Println("BilibiliSpider data len = 0")
		tool.SendWarn("ASO报警-B站 爬虫报错", "ASO报警-newRank-B站 抖音爬虫报错,错误信息：无数据")
	} else {
		err := db.SaveNewRankData(rs)
		if err != nil {
			return false, err
		}
	}
	return true, nil

}

func (this *BilibiliSpider) run() {
	defer func() {
		if err := recover(); err != nil {
			errMsg := string(debug.Stack())
			this.Println("[崩溃]err=", err, errMsg)
		}
	}()
	if conf.Conf.Debug.Switch {
		for _, fDate := range conf.Conf.Debug.DateList {
			this.Println("日期:", fDate, "开始采集数据...")
			succ, err := this.Collect(fDate)
			if succ {
				this.Println("日期:", fDate, "采集数据完成")
			} else {
				if err == nil {
					this.Println("日期:", fDate, "数据未更新")
				} else {
					this.Println("日期:", fDate, "出现错误err:", err)
				}
			}
		}
	} else {
	re:
		fDate := time.Now().Format("2006-01-02")
		this.GetDataByDate(fDate)
		time.Sleep(time.Duration(this.TimeInterval) * time.Hour)
		goto re
	}
}

func (this *BilibiliSpider) GetDataByDate(date string) {
	this.Println("日期:", date, "开始采集数据...")
	success, err := this.Collect(date)
	if err != nil {
		tool.SendWarn("ASO报警-B站 爬虫报错", "ASO报警-B站 爬虫报错,错误信息："+err.Error())
		this.Println("日期:", date, "采集失败 err=", err)
	} else {
		if success {
			this.Println("日期:", date, "采集数据完成 稍后再次采集...")
		} else {
			this.Println("日期:", date, "数据未更新 稍后重新采集...")
		}
	}
}
